// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Vector4Test.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Test.Robotics.Numerics
{
    using System;

    using Microsoft.Robotics.Numerics;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Vector4 unit test class
    /// </summary>
    [TestClass]
    public class Vector4Test
    {
        /// <summary>
        /// The Test context instance
        /// </summary>
        private TestContext testContextInstance;

        /// <summary>
        /// Initializes a new instance of the Vector4Test class.
        /// </summary>
        public Vector4Test()
        {
        }

        /// <summary>
        /// Gets or sets the test context which provides
        /// information about and functionality for the current test run.
        /// </summary>
        public TestContext TestContext
        {
            get
            {
                return this.testContextInstance;
            }

            set
            {
                this.testContextInstance = value;
            }
        }

        /// <summary>
        /// Test the default constructor
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4DefaultConstructor()
        {
            Vector4 v = new Vector4();
            Assert.IsTrue(
                v.X == 0.0 && v.Y == 0.0 && v.Z == 0.0 && v.W == 0.0, 
                "Vector4 Components are not zero in default constructor");
        }

        /// <summary>
        /// Test the constructor from double values
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4DoubleValuesConstructor()
        {
            Vector4 v = new Vector4(1.0, 2.0, 3.0, 4.0);
            Assert.IsTrue(
                v.X == 1.0 && v.Y == 2.0 && v.Z == 3.0 && v.W == 4.0, 
                "Vector4 Components are not what was passed into constructor");
        }

        /// <summary>
        /// Test the copy constructor
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4CopyConstructor()
        {
            Vector4 initialVector = new Vector4(1.0, 2.0, 3.0, 4.0);
            Vector4 v = new Vector4(initialVector);
            Assert.IsTrue(v.X == 1.0 && v.Y == 2.0 && v.Z == 3.0 && v.W == 4.0, "Vector4 copy constructor failed");
        }

        /// <summary>
        /// Tests the vector3 to vector4 constructor
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4CopyFromVector3Constructor()
        {
            Vector3 initialVector = new Vector3(1.0, 2.0, 3.0);
            Vector4 v = new Vector4(initialVector);
            Assert.IsTrue(
                v.X == 1.0 && v.Y == 2.0 && v.Z == 3.0 && v.W == 1.0, "Vector4 copy from Vector3 constructor failed");
        }

        /// <summary>
        /// Tests the addition operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4AdditionOperator()
        {
            Vector4 leftSide = new Vector4(10.0, 20.0, 30.0, 40.0);
            Vector4 rightSide = new Vector4(1.0, 2.0, 3.0, 4.0);
            Vector4 result = leftSide + rightSide;
            Assert.IsTrue(
                (Math.Abs(result.X - 11.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 22.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Z - 33.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.W - 44.0) < MathConstants.ErrorEpsilon), 
                "Vector4 addition operator failed");
        }

        /// <summary>
        /// Tests the matrix multiply operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix4Vector4MultiplicationOperator()
        {
            Matrix4 leftSide = new Matrix4();
            Vector4 rightSide = new Vector4(1.0, 2.0, 3.0, 4.0);

            leftSide[0, 0] = 10.0;
            leftSide[0, 1] = 20.0;
            leftSide[0, 2] = 30.0;
            leftSide[0, 3] = 40.0;

            leftSide[1, 0] = 100.0;
            leftSide[1, 1] = 200.0;
            leftSide[1, 2] = 300.0;
            leftSide[1, 3] = 400.0;

            leftSide[2, 0] = 1000.0;
            leftSide[2, 1] = 2000.0;
            leftSide[2, 2] = 3000.0;
            leftSide[2, 3] = 4000.0;

            leftSide[3, 0] = 10000.0;
            leftSide[3, 1] = 20000.0;
            leftSide[3, 2] = 30000.0;
            leftSide[3, 3] = 40000.0;

            Vector4 result = leftSide * rightSide;

            Assert.IsTrue(
                (Math.Abs(result.X - 300.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 3000.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Z - 30000.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.W - 300000.0) < MathConstants.ErrorEpsilon), 
                "Matrix4 Vector4 Mutiplication operator failed");
        }

        /// <summary>
        /// Test the ToString operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4ToString()
        {
            Vector4 vector = new Vector4(1.0, 2.0, 3.0, 4.0);
            string str = vector.ToString();

            // Not sure what the test should be
            Assert.IsTrue(str != null, "Vector4 tostring failed");
        }

        /// <summary>
        /// Test set from vector3
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4SetFromVector3()
        {
            Vector3 vector3 = new Vector3(1.0, 2.0, 3.0);
            Vector4 vector4 = new Vector4();
            vector4.SetFromVector3(vector3);
            Assert.IsTrue(
                vector4.X == vector3.X && vector4.Y == vector3.Y && vector4.Z == vector3.Z && vector4.W == 1.0, 
                "Vector4 set from vector3 failed");
        }

        /// <summary>
        /// Test set properties
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4SetComponents()
        {
            Vector4 vector = new Vector4();
            vector.X = 1.0;
            vector.Y = 2.0;
            vector.Z = 3.0;
            vector.W = 4.0;
            Assert.IsTrue(
                vector.X == 1.0 && vector.Y == 2.0 && vector.Z == 3.0 && vector.W == 4.0, 
                "Vector4 set properties failed");
        }

        /// <summary>
        /// Test get vector3
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4GetVector3()
        {
            Vector4 vector = new Vector4(1.0, 2.0, 3.0, 4.0);
            Vector3 vector3 = vector.GetVector3();
            Assert.IsTrue(vector3.X == 1.0 && vector3.Y == 2.0 && vector3.Z == 3.0, "Vector4 get vector3 failed");
        }

        /// <summary>
        /// Tests the minus operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4MinusOperator()
        {
            Vector4 initialVector = new Vector4(1.0, 2.0, 3.0, 4.0);
            Vector4 v = -initialVector;
            Assert.IsTrue(
                (Math.Abs(v.X + initialVector.X) < MathConstants.ErrorEpsilon)
                && (Math.Abs(v.Y + initialVector.Y) < MathConstants.ErrorEpsilon)
                && (Math.Abs(v.Z + initialVector.Z) < MathConstants.ErrorEpsilon)
                && (Math.Abs(v.W + initialVector.W) < MathConstants.ErrorEpsilon),
                "Vector4 minus operator failed");
        }

        /// <summary>
        /// Tests the subtraction operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4SubtractionOperator()
        {
            Vector4 leftSide = new Vector4(10.0, 20.0, 30.0, 40.0);
            Vector4 rightSide = new Vector4(1.0, 2.0, 3.0, 4.0);
            Vector4 result = leftSide - rightSide;
            Assert.IsTrue(
                (Math.Abs(result.X - 9.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 18.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Z - 27.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.W - 36.0) < MathConstants.ErrorEpsilon),
                "Vector4 subtraction operator failed");
        }

        /// <summary>
        /// Tests scalar divide
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4ScalarDivide()
        {
            Vector4 initialVector = new Vector4(10.0, 20.0, 30.0, 40.0);
            Vector4 result = initialVector / 2.0;
            Assert.IsTrue(
                (Math.Abs(result.X - 5.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 10.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Z - 15.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.W - 20.0) < MathConstants.ErrorEpsilon),
                "Vector4 scalar divide operator failed");
        }

        /// <summary>
        /// Tests scalar multiply
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4ScalarMultiply()
        {
            Vector4 initialVector = new Vector4(10.0, 20.0, 30.0, 40.0);
            Vector4 result = initialVector * 2.0;
            Assert.IsTrue(
                Math.Abs(result.X - 20.0) < MathConstants.ErrorEpsilon
                && Math.Abs(result.Y - 40.0) < MathConstants.ErrorEpsilon
                && Math.Abs(result.Z - 60.0) < MathConstants.ErrorEpsilon
                && Math.Abs(result.W - 80.0) < MathConstants.ErrorEpsilon,
                "Vector4 scalar multiply operator from right failed");
            result = 2.0 * initialVector;
            Assert.IsTrue(
                Math.Abs(result.X - 20.0) < MathConstants.ErrorEpsilon
                && Math.Abs(result.Y - 40.0) < MathConstants.ErrorEpsilon
                && Math.Abs(result.Z - 60.0) < MathConstants.ErrorEpsilon
                && Math.Abs(result.W - 80.0) < MathConstants.ErrorEpsilon,
                "Vector4 scalar multiply operator from left failed");
        }

        /// <summary>
        /// Tests getting vector length
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4VectorLength()
        {
            Vector4 initialVector = new Vector4(1.0, 2.0, 3.0, 4.0);
            double result = initialVector.Length();
            Assert.IsTrue(
                Math.Abs(result - Math.Sqrt(30.0)) < MathConstants.ErrorEpsilon, "Vector4 length calculation failed");
        }

        /// <summary>
        /// Tests vector normalization
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector4VectorNormalization()
        {
            Vector4 result;
            result = Vector4.Normalize(new Vector4(2.0, 0.0, 0.0, 0.0));

            Assert.IsTrue(Math.Abs(result.Length() - 1.0) <= MathConstants.ErrorEpsilon);

            result = Vector4.Normalize(new Vector4(2.0, 3.0, 4.0, 5.0));
            Assert.IsTrue(Math.Abs(result.Length() - 1.0) <= MathConstants.ErrorEpsilon);
        }
    }
}
